package com.szsfm.vehiclemanagement.tcp;

import android.annotation.SuppressLint;
import android.content.Context;
import android.text.TextUtils;

import com.orhanobut.logger.Logger;
import com.szsfm.vehiclemanagement.base64.Base64;
import com.szsfm.vehiclemanagement.bean.Latlng;
import com.szsfm.vehiclemanagement.bean.Message;
import com.szsfm.vehiclemanagement.bean.PushMessage;
import com.szsfm.vehiclemanagement.bean.User;
import com.szsfm.vehiclemanagement.bean.UserInfo;
import com.szsfm.vehiclemanagement.bean.Vehicle;
import com.szsfm.vehiclemanagement.event.CommEvent;
import com.szsfm.vehiclemanagement.event.LoginResultEvent;
import com.szsfm.vehiclemanagement.event.PushMessageEvent;
import com.szsfm.vehiclemanagement.manager.GreenDaoManager;
import com.szsfm.vehiclemanagement.util.ApkUtil;
import com.szsfm.vehiclemanagement.util.ByteUtil;
import com.szsfm.vehiclemanagement.util.DeviceUtil;
import com.szsfm.vehiclemanagement.util.NetworkUtil;

import org.greenrobot.eventbus.EventBus;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import static com.szsfm.vehiclemanagement.event.CommEvent.EVENT_TYPE_VEHICLE;

@SuppressLint("DefaultLocale")
public class TcpCore {

    private static final int WHAT_SEND_PING = 106;//发送心跳
    private static final int SEND_HEART_BEAT_TIME = 70 * 1000;//固定发送心跳时间
    public static int PORT = 31100;
    //public static int PORT = 20028;
    private static int SO_TIMEOUT = 15 * 1000;
    public static final int KEEP_ALIVE = 30 * 1000;// 正常的心跳时间

    public static int currentKeepAliveTime = KEEP_ALIVE;

    private static Context context;
    private static String host = "121.37.59.227";
    //   private static String host = "192.168.1.155";
    private int port = PORT;

    protected static final int CODE_OK = 0;// 成功
    protected static final int CODE_FAIL = 1;// 失败
    protected static final int CODE_ERR_MSG = 2;// 失败
    protected static final int CODE_NOT_SUPPORT = 3;// 不支持

    protected static final BlockingQueue<Packet> outQ = new LinkedBlockingQueue<Packet>();// 存储要发送的包

    public static AtomicBoolean isRunning = null;// 实现了多线程线程同步的原子性操作
    public static final AtomicBoolean isConnected = new AtomicBoolean(false);
    private volatile long lastReconnect;
    private volatile static Socket socket;
    private volatile InputStream is;
    private volatile static OutputStream os;
    private volatile static Thread writeThread;

    public static boolean enableConn = false;
    private String username;
    private String password;

    public TcpCore(Context context, String user, String password) {
        this(context, host, PORT, user, password);
    }

    public TcpCore(Context context, String host, int port, String user, String password) {
        this.context = context;
        this.host = host;
        this.port = port;
        isRunning = new AtomicBoolean(true);
        lastReconnect = 0;
        this.username = user;
        this.password = password;
    }

    public void loop() {
        Logger.v("TcpCore loop start,enableConn=" + enableConn);
        while (enableConn) {// 如果正在运行长连接
            try {
                if (!NetworkUtil.hasNetwork(context)) {
                    Logger.v("has no network, stop");
                    isRunning.set(false);//
                    break;
                }
                reconnect();// 重新初始化socket，重新连接
            } catch (InterruptedException e) {
                Logger.i("reconnect InterruptedException");
                isRunning.set(false);
                break;
            } catch (Throwable e) {
                Logger.v("connect error: " + e.toString() + ", retry...");
                lastReconnect = System.currentTimeMillis();
                isRunning.set(false);
                continue;
            }
            TcpUtil.unlock(context);
            processPackets();
        }
        try {
            stop();// while循环结束，即不在运行长连接，那么干掉socket
        } catch (InterruptedException e) {
            isRunning.set(false);
        }

        Logger.v("tcpservice loop end,enableConn=" + enableConn);
    }

    private void processPackets() {
        int timeoutRetry = 0;
        while (isRunning.get()) {// 是否长连接
            try {
                if (timeoutRetry > 0) {
                    Logger.v("retry over times, reconnecting" + timeoutRetry);
                    isRunning.set(false);
                    break;
                }
                Packet req;
                try {
                    Logger.v("阻塞准备读服务器数据");
                    req = Packet.decode(is);// 读取服务器数据
                    if (req == NULL_PACKET) {
                    }
                    // 收到服务器数据之后，重置发送心跳时间
                    if (currentKeepAliveTime != KEEP_ALIVE) {
                        currentKeepAliveTime = KEEP_ALIVE;
                    }
                } catch (SocketTimeoutException ex) {
                    ex.printStackTrace();
                    Logger.v("read packet timeout in retry " + timeoutRetry);
                    timeoutRetry++;
                    isRunning.set(false);
                    CommEvent ev = new CommEvent();
                    ev.type = CommEvent.EVENT_TYPE_SO_TIME_OUT;
                    EventBus.getDefault().post(ev);
                    continue;
                } catch (NullPointerException e) {// 如果输入流为空，则需重新联网
                    e.printStackTrace();
                    isRunning.set(false);
                    break;
                } catch (IOException i) {// 如果收到错误信息则继续接收数据
                    i.printStackTrace();
                    isRunning.set(false);
                    break;
                }
                Packet rsp = req.respond(this);
                if (NULL_PACKET != rsp) {
                    Logger.v("添加进包队列");
                    outQ.put(rsp);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                isRunning.set(false);
                break;
            } catch (Throwable e) {
                e.printStackTrace();
                Logger.v("receiving error: " + e.toString() + ", isRunning=" + isRunning.get() + " retry");
                lastReconnect = System.currentTimeMillis();
                break;
            }
        }
    }

    protected void stop() throws InterruptedException {
        onDisconnected();
        if (null != writeThread) {
            if (writeThread != null) {
                writeThread.interrupt();
            }
            writeThread = null;
        }
        if (null != socket) {
            try {
                socket.close();
            } catch (IOException e) {
            }
            socket = null;
        }
    }

    protected boolean sending() {
        try {
            Packet p = outQ.poll(currentKeepAliveTime, TimeUnit.MILLISECONDS);
            if (NULL_PACKET == p) {
                return true;
            }
            if (null == p) {
                p = new PingReq();
            }
            p.encode(os);
        } catch (InterruptedException e) {
            e.printStackTrace();
            Logger.i("outQ.poll InterruptedException");
            isRunning.set(false);
            return false;
        } catch (IOException e) {
            isRunning.set(false);
            Logger.i("outQ.poll获取数据超时IOException");
            e.printStackTrace();
            return false;
        }
        return true;
    }

    private void reconnect() throws InterruptedException, IOException {
        Logger.v("connecting to " + host + ":" + port);
        long now = System.currentTimeMillis();
        while (!isRunning.get() && now - lastReconnect < 5 * 1000) {// lastReconnect是发生IO错误那一刻
            Logger.v("wait 5s and retry");
            Thread.sleep(1000);
            now = System.currentTimeMillis();
        }
        if (isRunning.get()) {
            Logger.d("has connected ");
            return;
        }
        lastReconnect = now;
        if (!outQ.isEmpty()) {
            outQ.clear();// 清除掉开始给发的包了
        }
        if (null != writeThread) {
            writeThread.interrupt();
            writeThread = null;
        }

        try {
            if (is != null) {
                is.close();
                is = null;
            }
        } catch (Throwable e) {
        }

        try {
            if (os != null) {
                os.close();
                os = null;
            }
        } catch (Throwable e) {
        }

        try {
            if (socket != null && !socket.isClosed()) {
                socket.close();
                socket = null;
            }
        } catch (Throwable e) {
            Logger.v("socket关闭异常 " + e.getMessage());
        }
        Logger.v("reconnect()+准备连接new Socket();");
        socket = new Socket();
        socket.setSoTimeout(SO_TIMEOUT);
        socket.connect(new InetSocketAddress(host, port), 0);
        socket.setKeepAlive(true);
        Logger.v("connected to " + host + ":" + port);
        is = socket.getInputStream();
        os = socket.getOutputStream();

        Packet p = new ConnectReq(username, password, DeviceUtil.getImei(context), ApkUtil.getVersionname(context), "" + Latlng.shareInstance().getLatitue(), Latlng.shareInstance().getLongitue() + "");
        p.encode(os);
        os.flush();
        isRunning.set(true);
    }


    private static void addOutQ(Packet p) {
        if (outQ != null) {
            Logger.d("添加进发送包队列:cmd=" + p.cmd);
            try {
                if (p != null) {
                    if (isruning()) {
                        outQ.put(p);
                    } else {
                        Logger.d("网络已经中断,不放入发送队列");
                    }
                } else {
                    Logger.d("添加空包");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static void kot() {
        isRunning.set(false);
        isConnected.set(false);
        TcpCore.enableConn = false;
        TcpUtil.stopSavService(context);
    }

    public static boolean isruning() {
        return isRunning.get();
    }

    public synchronized void onConnected() {
        isConnected.set(true);
        if (writeThread == null) {
            writeThread = new Thread(new Writer(this));
            writeThread.start();
        }
    }

    public void onDisconnected() {
        isConnected.set(false);
    }

    private static String createPacketId() {
        long millis = System.currentTimeMillis();
        SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmssSSS");
        return sdf.format(millis);
    }

    private static final class Writer implements Runnable {

        private final TcpCore sav;

        public Writer(TcpCore sav) {
            this.sav = sav;
        }

        @Override
        public void run() {
            while (sav.sending()) ;
        }

    }

    private static final byte[] NULL_BUF = new byte[0];
    private static final Packet NULL_PACKET = new Packet(null) {

        @Override
        protected Packet dup() {
            return this;
        }

    };

    private static final Map<String, Packet> PACKET_FACTORY;

    static {
        PACKET_FACTORY = new TreeMap<>();
        PACKET_FACTORY.put(ConnectRsp.CMD, new ConnectRsp());//连接
        PACKET_FACTORY.put(QueryParamRsp.CMD, new QueryParamRsp());//查询参数
        PACKET_FACTORY.put(PushMsg.CMD, new PushMsg());//推送消息
        PACKET_FACTORY.put(UpdateMsgProcessReq.CMD, new UpdateMsgProcessReq());//更新消息已处理
        PACKET_FACTORY.put(KotMsg.CMD, new KotMsg());//踢掉客户端连接
        PACKET_FACTORY.put(QueryVehicleInfoRsp.CMD, new QueryVehicleInfoRsp());//查询车辆信息
        PACKET_FACTORY.put(CreateVehicleInfoRsp.CMD, new CreateVehicleInfoRsp());//上传车辆信息
       // PACKET_FACTORY.put(CreateVehiclePicRsp.CMD, new CreateVehiclePicRsp());//上传图片
    }

    public static abstract class Packet {
        protected static final String TITLE = "SFM";
        protected static final String SEP = "&";
        protected String cmd;
        protected byte[] payload;
        protected String PACKET_ID = null;

        public Packet(String cmd) {
            this(cmd, NULL_BUF);
        }

        public Packet(String cmd, byte[] payload) {
            if (null == cmd) {
                // throw new NullPointerException("cmd can not be null");
            }
            this.cmd = cmd;
            this.payload = payload;
        }

        public final void encode(final OutputStream os) throws IOException {
            if (os == null) {
                Logger.v(os == null ? "encode os true" : "encode os false");
                return;
            }

            String args;
            ByteArrayOutputStream baos;
            StringBuilder sb = new StringBuilder();
            synchronized (os) {
                baos = new ByteArrayOutputStream();
                baos.write(TITLE.getBytes());
                args = encodeArgs();
                int length = args.getBytes().length + cmd.length() + payload.length + 13;
                if (length==13)length=0;
                byte[] datalen = ByteUtil.int2Bytes(length);
                // int lengthdd = ByteUtil.getCount(datalen[0], datalen[1], datalen[2], datalen[3]);// 得到4个长度的字节
                baos.write(datalen);

                if (!TextUtils.isEmpty(cmd)&&length!=0) {
                    //                    baos.write(cmd.getBytes());
                    //                    baos.write(SEP);
                    sb.append(cmd);
                    sb.append(SEP);
                }

                if (TextUtils.isEmpty(PACKET_ID) && !TextUtils.isEmpty(args)) PACKET_ID = createPacketId();
                if (!TextUtils.isEmpty(PACKET_ID)&&length!=0) {
                    // baos.write(PACKET_ID.getBytes());
                    sb.append(PACKET_ID);
                }
                if (length!=0)
                Logger.v("发送的命令：" + TITLE + ByteUtil.int0(length) + ByteUtil.int1(length) + ByteUtil.int2(length) + ByteUtil.int3(length) + cmd + "&" + (TextUtils.isEmpty(PACKET_ID) ? "" : PACKET_ID) + "&" + args.toString() + " 长度:" + (length == 13 ? "" : length));
                else
                    Logger.v("发送的命令：" + TITLE + ByteUtil.int0(length) + ByteUtil.int1(length) + ByteUtil.int2(length) + ByteUtil.int3(length));
                if (!TextUtils.isEmpty(args)&&length!=0) {//args为空的都是心跳包
                    //                    baos.write(SEP);
                    //                    baos.write(args.getBytes());
                    sb.append(SEP);
                    sb.append(args);
                    //                    Resend resend = new Resend();
                    //                    resend.put(PACKET_ID, this, new Resend.TimerDelayListener() {
                    //                        @Override
                    //                        public void timeout(Packet packet) {
                    //                            if (writeThread != null && writeThread.isAlive()) {
                    //                                addOutQ(packet);
                    //                            }
                    //                        }
                    //                    });
                }
                if (payload.length > 0) {
                    baos.write(payload);
                }
            }
            if (baos != null) {
                String base64Str = Base64.encode(sb.toString());
                byte[] argsBuf = base64Str.getBytes();
                baos.write(argsBuf);
                os.write(baos.toByteArray());
                os.flush();
            }
        }

        public static final Packet decode(InputStream is) throws NullPointerException, IOException {

            if (null == is) {
                throw new NullPointerException("InputStream can not be null");
            }

            byte[] buf = new byte[7];
            int n = 0;
            boolean found = false;
            int b;
            while (true) {
                b = is.read();
                if (-1 == b) {// 读到末尾
                    break;
                }
                if (n == 6) {
                    found = true;
                    break;
                }
                buf[n++] = (byte) b;
            }
            if (found) {//35312D00
                int length = ByteUtil.getCount(buf[3], buf[4], buf[5], buf[6]);// 得到4个长度的字节
                Logger.v("收到命令:" + new String(buf, 0, n) + " 长度:" + length);
                if (length == 0 || length > (10 <<10)) {
                    return NULL_PACKET;
                }//
                byte[] temp = new byte[length];
                n = 0;
                while (n < temp.length) {
                    int m = is.read(temp, n, temp.length - n);
                    if (-1 == m) {
                        Logger.i("read end");
                        break;
                    }
                    n += m;
                }
                temp = Base64.decodeToByteArray(new String(temp));
                //int[] data=ByteUtil.unsign2int(temp);
                String pdu = new String(temp, 0, temp.length, "UTF-8");
                String[] sa = pdu.split("[&]");
                Logger.v("总长度:" + n + " 命令的文本：" + pdu + " 长度:" + pdu.length());
                Packet p = PACKET_FACTORY.get(sa[0]);
                if (p != null) {
                    p.cmd = sa[0];
                    p.PACKET_ID = sa[1];
                    //    int ret = Resend.remove(p.PACKET_ID);
                    //   Logger.d("移除重发的包:" + ret);
                    p.decodeArgs(sa, 2, sa.length);
                } else {
                    p = NULL_PACKET;
                    Logger.v(sa[0] + ",命令不存在，不处理此命令...");
                }
                return p;
            } else {
                //Logger.d("header too long or reset by peer, length="+n);
                throw new IOException("header too long or reset by peer, length=" + n+" b="+b);
            }
        }

        public Packet respond(TcpCore sav) {
            return NULL_PACKET;
        }

        protected String encodeArgs() {
            return "";
        }

        protected void decodeArgs(String[] sa, int offset, int length) {
        }

        protected abstract Packet dup();
    }

    /**
     * 查询基本信息
     */
    public static void queryParam(String userid) {
        addOutQ(new QueryParamReq(userid));
    }

    /**
     * 文本已读
     */
    public static void updateMessageRead(List<Long> lst) {
        addOutQ(new UpdateMsgReadReq(lst));
    }

    /**
     * 文本已处理
     */
    public static void updateMessageProcess() {
        addOutQ(new UpdateMsgProcessReq());
    }

    /**
     * 查询车辆信息
     */
    public static void queryVehicleInfo(String s) {
        addOutQ(new QueryVehicleInfoReq(s));
    }

    /**
     * 上传车辆违法信息
     * [{"plate":"YBJ4532","lat":22.151651,"lng":114.855551,"time":20170532121556","type":1}]
     */
    public static void updateVehicleInfo(String plate,double lat,double lng,String time,int type,String[] fs) {
        addOutQ(new CreateVehicleInfoReq(plate,lat,lng,time,type,fs));
    }

    /**
     * 上传图片
     */
    public static void updateImage() {
        addOutQ(new CreateVehiclePicReq());
    }

    public static class PingReq extends Packet {
        protected static final String CMD = "";

        public PingReq() {
            super(CMD);
        }

        @Override
        protected Packet dup() {
            return this;
        }
    }

    public static class ConnectReq extends Packet {
        protected static final String CMD = "REQ";
        protected String user;
        protected String pass;
        protected String versionName;
        protected String imei;
        protected String lat;
        protected String lng;

        public ConnectReq() {
            super(CMD);
        }

        public ConnectReq(String user, String pass, String imei, String versionName, String lat, String lng) {
            super(CMD);
            this.user = user;
            this.pass = pass;
            this.imei = imei;
            this.lat = lat;
            this.lng = lng;
            this.versionName = versionName;
        }

        @Override
        protected Packet dup() {
            return new ConnectReq();
        }

        @Override
        protected String encodeArgs() {
            //SFM82000REQ&1513644626193&1&1.0.0&王三&123456789&Android 6.0.1|1.0|355905071257159&22.527335&114.042901
            return String.format("1&1.0.0&%s&%s&Android %s|%s|%s&%s&%s", user, pass, android.os.Build.VERSION.RELEASE, versionName, imei, lat, lng);
        }

    }

    //ARE&171220101641321&0&1.0.0&1EA95B9D-16F4-44B8-B163-1BADDF84FECC&171220101639&624
    //连接即登录
    public static class ConnectRsp extends Packet {
        protected static final String CMD = "ARE";
        protected String code;
        protected String version;
        private String userId;
        private String time;
        private String token;
        private String messageJson;
        private String error;
        private String errorMsg;
        LoginResultEvent event;

        public ConnectRsp() {
            super(CMD);
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected void decodeArgs(String[] sa, int offset, int length) {
            code = sa[offset++];//返回码
            event = new LoginResultEvent();
            if ((CODE_OK + "").equals(code)) {
                version = sa[offset++];//协议版本
                userId = sa[offset++];//用户ID
                time = sa[offset++];//用户在服务端的登录时间
                token = sa[offset++];//会话ID，服务端标识此次客户端和服务端的连接。
            } else {
                version = sa[offset++];//协议版本
                errorMsg = sa[offset++];//错误消息
            }
        }

        @Override
        public Packet respond(TcpCore tcp) {
            Logger.i("code==" + code);
            event.ret = code;
            if ((CODE_OK + "").equals(code)) {
                User user = User.shareInstance();
                user.setUserID(userId);
                user.setLoginTime(time);
                currentKeepAliveTime = KEEP_ALIVE;
                tcp.onConnected();
            } else {
                event.msg = errorMsg;
                kot();
            }
            EventBus.getDefault().post(event);
            return super.respond(tcp);
        }
    }

    //R_Q_PARAM&76c05320-330 3.2.2.4响应查询基本信息(R_Q_PARAM)
    public static class QueryParamReq extends Packet {
        protected static final String CMD = "Q_PARAM";
        protected String userid;

        public QueryParamReq() {
            super(CMD);
        }

        public QueryParamReq(String userid) {
            super(CMD);
            this.userid = userid;
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected String encodeArgs() {
            return "" + this.userid;
        }

    }

    //3.2.2.4响应查询基本信息(R_Q_PARAM)
    public static class QueryParamRsp extends Packet {
        protected static final String CMD = "R_Q_PARAM";
        protected String code;
        private String json;
        private String errorMsg;

        public QueryParamRsp() {
            super(CMD);
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected void decodeArgs(String[] sa, int offset, int length) {
            code = sa[offset++];//返回码
            CommEvent event = new CommEvent();
            if ((CODE_OK + "").equals(code)) {
                json = sa[offset++];//json字符串
                event.ret = 0;
                event.type = CommEvent.EVENT_TYPE_PARAM;
            } else {
                errorMsg = sa[offset++];//错误信息
            }
            EventBus.getDefault().post(event);
        }

        @Override
        public Packet respond(TcpCore tcp) {
            Logger.i("code==" + code);
            if ((CODE_OK + "").equals(code)) {
                try {
                    JSONObject object = new JSONObject(json);
                    String city = object.getString("city");
                    JSONArray arr = object.getJSONArray("road");
                    List<String> roads = new ArrayList<>();
                    for (int i = 0; i < arr.length(); i++) {
                        String road = arr.getString(i);
                        roads.add(road);
                    }
                    UserInfo info = UserInfo.shareInstance();
                    info.setCity(city);
                    info.setRoads(roads);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            } else {

            }
            return super.respond(tcp);
        }
    }

    //3.2.2.5服务端推送文本消息(P_MSG)
    public static class PushMsg extends Packet {
        protected static final String CMD = "P_MSG";
        protected String timestamp;
        protected String json;

        public PushMsg() {
            super(CMD);
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected void decodeArgs(String[] sa, int offset, int length) {
            timestamp = sa[offset++];
            json = sa[offset++];//json字符串
        }

        @Override
        public Packet respond(TcpCore tcp) {
            Logger.i("timestamp==" + timestamp);
            Logger.i("json==" + json);
            PushMessage pushMessage = new PushMessage();
            try {
                JSONObject object = new JSONObject(json);
                String timestamp = object.getString("timestamp");
                pushMessage.setTimestamp(timestamp);
                List<Message> msgs = new ArrayList<>();
                JSONArray array = object.getJSONArray("data");
                for (int i = 0; i < array.length(); i++) {
                    JSONObject msgObj = array.getJSONObject(i);
                    long msgid = msgObj.getLong("id");
                    int type = msgObj.getInt("type");
                    String title = msgObj.getString("title");
                    String message = msgObj.getString("message");
                    String st = msgObj.getString("timestamp");
                    Message msg = new Message();
                    msg.setMsgId(msgid);
                    msg.setType(type);
                    msg.setTitle(title);
                    msg.setMessage(message);
                    msg.setSubTimestamp(st);
                    msgs.add(msg);
                }
                pushMessage.setData(msgs);
            } catch (Exception e) {
                e.printStackTrace();
            }
            GreenDaoManager.getInstance().getPushMessageDao().insert(pushMessage);
            PushMessageEvent event = new PushMessageEvent();
            event.pushMessage = pushMessage;
            EventBus.getDefault().post(event);
            return super.respond(tcp);
        }
    }


    //3.2.2.6标识推送文本消息已读(U_MSG_READ)
    public static class UpdateMsgReadReq extends Packet {
        protected static final String CMD = "U_MSG_READ";

        public List<Long> ids;

        public UpdateMsgReadReq() {
            super(CMD);
        }

        public UpdateMsgReadReq(List<Long> ids) {
            super(CMD);
            this.ids = ids;
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected String encodeArgs() {
            JSONArray arr = new JSONArray();
            for (int i=0;i<ids.size();i++){
                arr.put(ids.get(i));
            }
            JSONObject obj = new JSONObject();
            try {
                obj.put("ids",arr.toString());
            }catch (JSONException e){
                e.printStackTrace();
            }
            return obj.toString();
        }

    }

    //3.2.2.7标识文本消息已处理(U_MSG_PROCESS)
    public static class UpdateMsgProcessReq extends Packet {
        protected static final String CMD = "U_MSG_PROCESS";

        public long[] ids;

        public UpdateMsgProcessReq() {
            super(CMD);
        }

        public UpdateMsgProcessReq(long[] ids) {
            super(CMD);
            this.ids = ids;
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected String encodeArgs() {
            return "";
        }
    }

    //3.2.2.9服务器发送断开链接消息(KOT)
    public static class KotMsg extends Packet {
        protected static final String CMD = "KOT";
        protected String json;

        public KotMsg() {
            super(CMD);
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected void decodeArgs(String[] sa, int offset, int length) {
            json = sa[offset++];//json字符串
        }

        @Override
        public Packet respond(TcpCore tcp) {
            Logger.i("json==" + json);
            kot();
            return super.respond(tcp);
        }
    }

    //3.2.2.10 查询车辆信息(Q_VI)
    public static class QueryVehicleInfoReq extends Packet {
        protected static final String CMD = "Q_VI";
        protected String plateNo;

        public QueryVehicleInfoReq() {
            super(CMD);
        }

        public QueryVehicleInfoReq(String plateNo) {
            super(CMD);
            this.plateNo = plateNo;
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected String encodeArgs() {
            return String.format("{\"plate\":\"%s\"}", this.plateNo);
        }

    }

    // 3.2.2.10 查询车辆信息(Q_VI)
    public static class QueryVehicleInfoRsp extends Packet {
        protected static final String CMD = "R_Q_VI";
        protected String code;
        private String json;
        private String errorMsg;

        public QueryVehicleInfoRsp() {
            super(CMD);
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected void decodeArgs(String[] sa, int offset, int length) {
            code = sa[offset++];//返回码
            if ((CODE_OK + "").equals(code)) {
                json = sa[offset++];//json字符串
            } else {
                errorMsg = sa[offset++];//错误信息
            }
        }

        @Override
        public Packet respond(TcpCore tcp) {
            Logger.i("code==" + code);
            if ((CODE_OK + "").equals(code)) {
                try {
                    JSONObject object = new JSONObject(json);
                    String plate = object.getString("plate");
                    String no = object.getString("no");
                    JSONArray array = object.getJSONArray("carInfo");
                    List<Vehicle> lst = new ArrayList<>();
                    for (int i = 0; i < array.length(); i++) {
                        JSONObject vehicleObj = array.getJSONObject(i);
                        String place = vehicleObj.getString("place");
                        String time = vehicleObj.getString("time");
                        double lat = vehicleObj.getDouble("lat");
                        double lng = vehicleObj.getDouble("lng");
                        String type = vehicleObj.getString("type");
                        Vehicle vehicle = new Vehicle();
                        vehicle.setLat(lat);
                        vehicle.setLng(lng);
                        vehicle.setPlace(place);
                        vehicle.setTime(time);
                        vehicle.setType(type);
                        vehicle.setPlate(plate);
                        lst.add(vehicle);
                    }
                    if (!lst.isEmpty()){
                        CommEvent cm = new CommEvent();
                        cm.obj = lst;
                        cm.type = EVENT_TYPE_VEHICLE;
                        cm.ret = 0;
                        EventBus.getDefault().post(cm);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }

            } else {

            }
            return super.respond(tcp);
        }
    }


    //3.2.2.12 上传车辆违法信息(U_VI)
    public static class CreateVehicleInfoReq extends Packet {
        protected static final String CMD = "U_VI";
        protected String json="";
        String p;
        double lat,lng;
        String t;
        int type;
        protected String[] fns;

        public CreateVehicleInfoReq(String plate,double lat,double lng,String time,int type,String[] fs) {
            super(CMD);
            this.p= plate;
            this.lat = lat;
            this.lng = lng;
            this.t = time;
            this.type = type;
            this.fns = fs;
        }

        public CreateVehicleInfoReq(String json) {
            super(CMD);
            this.json = json;
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected String encodeArgs() {
            //U_VI&76c05320-3&123456&[{"plate":"YBJ4532","lat":22.151651,"lng":114.855551,"time":20170532121556","type":1,"filenames":["123.jpg","abc.jpg","a1c.jpg"]}]
            try {
                JSONArray arr = new JSONArray();
                JSONObject obj = new JSONObject();
                obj.put("plate",this.p);
                obj.put("lat",this.lat);
                obj.put("lng",this.lng);
                obj.put("time",this.t);
                obj.put("type",this.type);
                JSONArray fArr = new JSONArray();
                for (int i=0;i<fns.length;i++){
                    fArr.put(fns[i]);
                }
                obj.put("filenames",fArr);
                arr.put(obj);
                json = arr.toString();
            }catch (JSONException e){
                e.printStackTrace();
            }
            return json;
        }

    }

    // 3.2.2.13 响应上传车辆违法信息(R_U_VI)
    public static class CreateVehicleInfoRsp extends Packet {
        protected static final String CMD = "R_U_VI";
        protected String code;
        private String errorMsg;
        protected String json;
        protected String plate;
        protected long lat;
        protected long lng;
        protected String time;
        protected int type;

        public CreateVehicleInfoRsp() {
            super(CMD);
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected void decodeArgs(String[] sa, int offset, int length) {
            code = sa[offset++];//返回码
            CommEvent ev = new CommEvent();
            ev.type = CommEvent.EVENT_TYPE_CREATE_VI;
            if ((CODE_OK + "").equals(code)) {
                ev.ret = 0;
            } else {
                ev.ret = 1;
                errorMsg = sa[offset++];//错误信息
            }
            EventBus.getDefault().post(ev);
        }

        @Override
        public Packet respond(TcpCore tcp) {
            Logger.i("code==" + code);
            if ((CODE_OK + "").equals(code)) {

            } else {

            }
            return super.respond(tcp);
        }
    }


    //3.2.2.14 上传图片(C_V_PIC)
    public static class CreateVehiclePicReq extends Packet {
        protected static final String CMD = "C_V_PIC";
        protected String json;
        protected String plate;
        protected String[] filenames;

        public CreateVehiclePicReq() {
            super(CMD);
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected String encodeArgs() {
            try {
                JSONObject object = new JSONObject();
                object.put("plate", plate);
                JSONArray array = new JSONArray();
                for (int i = 0; i < filenames.length; i++) {
                    array.put("" + filenames[i]);
                }
                object.put("filenames", array.toString());
                this.json = object.toString();
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return this.json;
        }

    }


    // 3.2.2.13 响应上传车辆违法信息(R_U_VI)
    public static class CreateVehiclePicRsp extends Packet {
        protected static final String CMD = "R_U_VI";
        protected String code;
        private String json;
        private String errorMsg;

        public CreateVehiclePicRsp() {
            super(CMD);
        }

        @Override
        protected Packet dup() {
            return this;
        }

        @Override
        protected void decodeArgs(String[] sa, int offset, int length) {
            code = sa[offset++];//返回码
            if ((CODE_OK + "").equals(code)) {
            } else {
                errorMsg = sa[offset++];//错误信息
            }
        }

        @Override
        public Packet respond(TcpCore tcp) {
            Logger.i("code==" + code);
            if ((CODE_OK + "").equals(code)) {

            } else {

            }
            return super.respond(tcp);
        }
    }

}
